package rmi;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.rmi.AccessException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

import data.Data;
import data.DroitAcces;
import data.Medecin;
import data.Medicament;
import data.Ordonnance;
import data.Patient;

import annotation.Id;
import annotation.Ignore;
import annotation.Table;
import base.Base;

public class ServeurRmi implements ServeurRmiInterface {
	Base base = new Base();
	
	/**
	 * Procédure principale du serveur RMI.
	 * Créé le serveur et le lance.
	 * @param args
	 * Aucun argument requis.
	 */
	public static void main(String[] args){
		ServeurRmi serveur = new ServeurRmi();
		serveur.lancer();
	}

	/**
	 * Lance le serveur RMI.
	 */
	public void lancer() {
		int port = 53000;
		
		try {
			ServeurRmiInterface stub = (ServeurRmiInterface) UnicastRemoteObject.exportObject(this,0);
			LocateRegistry.createRegistry(port);
			Registry registry = LocateRegistry.getRegistry(port);
			registry.rebind("serveurRMI", stub);
			System.out.println("Serveur RMI démarré");
		} catch (AccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public boolean authentifier(String id, String pwd) throws RemoteException {
		boolean res = false;
		base.ouvrir();
		
		String query = "SELECT count(*) FROM medecin WHERE id_medecin = ? AND mot_de_passe_medecin = ?";
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setString(1, id);
			ps.setString(2, pwd);
			
			ResultSet rs = ps.executeQuery();
			rs.next();
			res = (rs.getInt(1) == 1)? true : false;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			res = false;
		}
		
		try {if (ps != null) ps.close();} catch (Exception e) {
			System.out.println("ServeurRmi erreur : " + e.getMessage());
		}
		
		base.fermer();
		return res;
	}

	@Override
	public int stocker(Data data) throws RemoteException {
		int res = -1;
		base.ouvrir();
		
		String table = null;
		@SuppressWarnings("rawtypes")
		Class c = data.getClass();
		@SuppressWarnings("unchecked")
		Table ann = (Table)c.getAnnotation(Table.class);
		table = ann.name();
		Field[] fields = c.getDeclaredFields();
		String attrQuery = "(";
		String valQuery = " VALUES (";
		int num = 0;
		
		for (Field field : fields) { 
			if (!field.isAnnotationPresent(Ignore.class) && (!field.isAnnotationPresent(Id.class) || (field.isAnnotationPresent(Id.class) && !field.getAnnotation(Id.class).autoIncrement()))){
				if (num > 0) {
					attrQuery += ", ";
					valQuery += ", ";
				}
				num++;
				attrQuery += field.getName();
				String nomGetter = "get" + 
					field.getName().substring(0, 1).toUpperCase() +
					field.getName().substring(1);
				
				try {
					@SuppressWarnings("unchecked")
					Method mGetter = c.getMethod(nomGetter);
					if(field.getType() == String.class){
						valQuery += "'" + mGetter.invoke(data) +"'";
					}else{
						valQuery += mGetter.invoke(data);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return -1;
				}
			}
		}
		attrQuery += ")";
		valQuery += ")";
		String query = "INSERT INTO " + table + attrQuery + valQuery;
		System.out.println("query = "+query);
		
		Statement st;
		try {
			st = base.getConnection().createStatement();
			
			res = st.executeUpdate(query);
			if (st != null) st.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -1;
		}

		base.fermer();
		return res;
	}

	@Override
	public boolean modifier(Data data) throws RemoteException {
		base.ouvrir();

		String table = null;
		@SuppressWarnings("rawtypes")
		Class c = data.getClass();
		@SuppressWarnings("unchecked")
		Table ann = (Table)c.getAnnotation(Table.class);
		table = ann.name();
		Field[] fields = c.getDeclaredFields();
		String setQuery = " SET ";
		String whereQuery = " WHERE ";
		int numS = 0;
		int numW = 0;
		
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Ignore.class) && !field.isAnnotationPresent(Id.class)){
				if (numS > 0) setQuery += ", ";
				numS++;
				setQuery += field.getName() + " = ";
				String nomGetter = "get" + 
					field.getName().substring(0, 1).toUpperCase() +
					field.getName().substring(1);
				
				try {
					@SuppressWarnings("unchecked")
					Method mGetter = c.getMethod(nomGetter);
					if(field.getType() == String.class){
						setQuery += "'" + mGetter.invoke(data) +"'";
					}else{
						setQuery += mGetter.invoke(data);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				}
			}else if(field.isAnnotationPresent(Id.class)){
				if (numW > 0) whereQuery += " AND ";
				numW++;
				whereQuery += field.getName() + " = ";
				String nomGetter = "get" + 
					field.getName().substring(0, 1).toUpperCase() +
					field.getName().substring(1);
				
				try {
					@SuppressWarnings("unchecked")
					Method mGetter = c.getMethod(nomGetter);
					if(field.getType() == String.class){
						whereQuery += "'" + mGetter.invoke(data) +"'";
					}else{
						whereQuery += mGetter.invoke(data);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				}
			}
		}
		String query = "UPDATE " + table + setQuery + whereQuery;
		System.out.println("query = "+query);
		
		Statement st;
		try {
			st = base.getConnection().createStatement();
			
			st.executeUpdate(query);
			if (st != null) st.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		base.fermer();
		return true;
	}

	@Override
	public ArrayList<Patient> listerPatients(String numero_securite_sociale, String nom, String prenom,int debut, int nombre)
			throws RemoteException {
		ArrayList<Patient> res = new ArrayList<Patient>();
		boolean change = false;
		base.ouvrir();
		
		String query = "SELECT * FROM patient";
		
		if(numero_securite_sociale != null && !numero_securite_sociale.equals("")){
			query += " WHERE numero_securite_sociale_patient LIKE '%" + numero_securite_sociale + "%'";
			change = true;
		}
		if(nom != null && !nom.equals("")){
			if(change)	query += " AND";
			else		query += " WHERE";
			query += " nom_patient LIKE '%" + nom + "%'";
			change = true;
		}
		if(prenom != null && !prenom.equals("")){
			if(change)	query += " AND ";
			else		query += " WHERE";
			query += " prenom_patient LIKE '%" + prenom + "%'";
			change = true;
		}
		
		query += " ORDER BY nom_patient ASC, prenom_patient ASC LIMIT ?, ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setInt(1, debut);
			ps.setInt(2, nombre);
			
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				Patient patient = new Patient(rs.getString("nom_patient"),
						rs.getString("prenom_patient"),
						rs.getString("sexe_patient"),
						rs.getString("date_naissance_patient"),
						rs.getDouble("taille_patient"),
						rs.getDouble("poids_patient"),
						rs.getString("adresse_patient"),
						rs.getInt("code_postal_patient"),
						rs.getString("ville_patient"),
						rs.getString("photo_patient"),
						rs.getString("numero_securite_sociale_patient"),
						rs.getString("numero_telephone_patient"),
						rs.getString("numero_portable_patient"),
						rs.getString("email_patient"));
				res.add(patient);
			}
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}
	
	@Override
	public ArrayList<Medecin> listerMedecins(String numero_enregistrement, String nom, String prenom,int debut, int nombre) throws RemoteException {
		ArrayList<Medecin> res = new ArrayList<Medecin>();
		boolean change = false;
		base.ouvrir();
		
		String query = "SELECT * FROM medecin";
		
		if(numero_enregistrement != null && !numero_enregistrement.equals("")){
			query += " WHERE id_medecin LIKE '%" + numero_enregistrement + "%'";
			change = true;
		}
		if(nom != null && !nom.equals("")){
			if(change)	query += " AND";
			else		query += " WHERE";
			query += " nom_medecin LIKE '%" + nom + "%'";
			change = true;
		}
		if(prenom != null && !prenom.equals("")){
			if(change)	query += " AND ";
			else		query += " WHERE";
			query += " prenom_medecin LIKE '%" + prenom + "%'";
			change = true;
		}
		
		query += " ORDER BY nom_medecin ASC, prenom_medecin ASC LIMIT ?, ?";
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setInt(1, debut);
			ps.setInt(2, nombre);
			
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				Medecin medecin = new Medecin(rs.getString("id_medecin"),
						rs.getString("nom_medecin"),
						rs.getString("prenom_medecin"),
						rs.getString("mot_de_passe_medecin"),
						rs.getString("id_droit_acces"),
						rs.getString("adresse_medecin"),
						rs.getString("code_postal"),
						rs.getString("ville"),
						rs.getString("numero_telephone_medecin"),
						rs.getString("numero_portable_medecin"),
						rs.getString("email_medecin"));
				res.add(medecin);
			}
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}
	
	@Override
	public Patient getPatient(String numero_securite_sociale)
			throws RemoteException {
		Patient res = null;
		base.ouvrir();
		
		String query = "SELECT * FROM patient WHERE numero_securite_sociale_patient = ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setString(1, numero_securite_sociale);
			
			ResultSet rs = ps.executeQuery();
			rs.next();
			res = new Patient(rs.getString("nom_patient"),
					rs.getString("prenom_patient"),
					rs.getString("sexe_patient"),
					rs.getString("date_naissance_patient"),
					rs.getDouble("taille_patient"),
					rs.getDouble("poids_patient"),
					rs.getString("adresse_patient"),
					rs.getInt("code_postal_patient"),
					rs.getString("ville_patient"),
					rs.getString("photo_patient"),
					rs.getString("numero_securite_sociale_patient"),
					rs.getString("numero_telephone_patient"),
					rs.getString("numero_portable_patient"),
					rs.getString("email_patient"));
			
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}
	@Override
	public int nbEnregistrements(@SuppressWarnings("rawtypes") Class c) throws RemoteException{
		int res = 0;
		base.ouvrir();

		@SuppressWarnings("unchecked")
		Table ann = (Table)c.getAnnotation(Table.class);
		
		String query = "SELECT count(*) FROM " + ann.name();
		
		PreparedStatement ps;
		try {
			ps = base.getConnection().prepareStatement(query);

			ResultSet rs = ps.executeQuery();
			rs.next();
			res = rs.getInt(1);
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return -1;
		}
		
		base.fermer();
		return res;
	}
	
	/**
	 * @deprecated
	 * utiliser nbEnregistrement(Patient.class) à la place
	 */
	@Deprecated
	@Override
	public int nbPatients() throws RemoteException {
		return nbEnregistrements(Patient.class);
	}

	@Override
	public boolean supprimerPatient(String numero_securite_sociale)
			throws RemoteException {
		boolean res = false;
		base.ouvrir();
		
		String query = "DELETE FROM patient WHERE numero_securite_sociale_patient = ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setString(1, numero_securite_sociale);
			
			res = (ps.executeUpdate() > 0)? true : false;
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			res = false;
		}
		
		base.fermer();
		
		return res;
	}
	
	public ArrayList<Medicament> listerMedicaments(String nom_medicament) throws RemoteException {
		ArrayList<Medicament> res = new ArrayList<Medicament>();
		base.ouvrir();
		
		String query = "SELECT * FROM medicament";
		
		if(nom_medicament != null && !nom_medicament.equals("")){
			query += " WHERE nom_medicament LIKE '%" + nom_medicament + "%'";
		}
		
		query += " ORDER BY nom_medicament ASC";
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				Medicament medicament = new Medicament(rs.getInt("id_medicament"),
						rs.getString("nom_medicament"),
						rs.getString("description_medicament"),
						rs.getString("quantite_medicament"),
						rs.getString("frequence_medicament"),
						rs.getString("administration_medicament"),
						rs.getString("categorie_therapeutique_medicament"));
				res.add(medicament);
			}
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}
	
	@Override
	public Medicament getMedicament(Integer id_medicament)
			throws RemoteException {
		Medicament res = null;
		base.ouvrir();
		
		String query = "SELECT * FROM medicament WHERE id_medicament = ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setInt(1, id_medicament);
			
			ResultSet rs = ps.executeQuery();
			rs.next();
			res = new Medicament(rs.getInt("id_medicament"),
					rs.getString("nom_medicament"),
					rs.getString("description_medicament"),
					rs.getString("quantite_medicament"),
					rs.getString("frequence_medicament"),
					rs.getString("administration_medicament"),
					rs.getString("categorie_therapeutique_medicament"));
			
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}

	@Override
	public Medecin getMedecin(String id_medecin) throws RemoteException {
		// TODO Auto-generated method stub
		Medecin res = null;
		base.ouvrir();
		
		String query = "SELECT * FROM medecin WHERE id_medecin = ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setString(1, id_medecin);
			
			ResultSet rs = ps.executeQuery();
			rs.next();
			res = new Medecin(rs.getString("id_medecin"),
					rs.getString("nom_medecin"),
					rs.getString("prenom_medecin"),
					rs.getString("mot_de_passe_medecin"),
					rs.getString("id_droit_acces"),
					rs.getString("adresse_medecin"),
					rs.getString("code_postal"),
					rs.getString("ville"),
					rs.getString("numero_telephone_medecin"),
					rs.getString("numero_portable_medecin"),
					rs.getString("email_medecin"));
			
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}

	@Override
	public ArrayList<Ordonnance> listerOrdonnances(
			String numero_securite_sociale_patient, String date_ordonnance, int debut, int nombre)
			throws RemoteException {
		ArrayList<Ordonnance> res = new ArrayList<Ordonnance>();
		boolean change = false;
		base.ouvrir();
		
		String query = "SELECT * FROM ordonnance";
		
		if(numero_securite_sociale_patient != null && !numero_securite_sociale_patient.equals("")){
			query += " WHERE numero_securite_sociale_patient LIKE '%" + numero_securite_sociale_patient + "%'";
			change = true;
		}
		if(date_ordonnance != null && !date_ordonnance.equals("")){
			if(change)	query += " AND";
			else		query += " WHERE";
			query += " date_ordonnance LIKE '%" + date_ordonnance + "%'";
			change = true;
		}
		
		query += " ORDER BY date_ordonnance DESC,id_ordonnance DESC LIMIT ?, ?";
		
		System.out.println("query = "+query);
		
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setInt(1, debut);
			ps.setInt(2, nombre);
			
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				Ordonnance ordonnance = new Ordonnance(rs.getInt("id_ordonnance"),
						rs.getString("date_ordonnance"),
						rs.getString("description_ordonnance"),
						rs.getString("numero_securite_sociale_patient"),
						rs.getString("id_medecin"));
				res.add(ordonnance);
			}
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}

	@Override
	public ArrayList<Medicament> listerMedicamentsOrdonnance(Integer id_ordonnance) throws RemoteException {
		/**
		 * SELECT *
		 * FROM posologie p
		 * INNER JOIN medicament m ON p.id_medicament = m.id_medicament
		 * WHERE id_ordonnance =1
		 */
		ArrayList<Medicament> res = new ArrayList<Medicament>();
		base.ouvrir();
		
		String query = "SELECT * FROM posologie p" +
				" INNER JOIN medicament m" +
				" ON p.id_medicament = m.id_medicament" +
				" WHERE id_ordonnance = ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setInt(1, id_ordonnance);
			
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				Medicament medicament = new Medicament(rs.getInt("id_medicament"),
						rs.getString("nom_medicament"),
						rs.getString("description_medicament"),
						rs.getString("quantite_posologie"),
						rs.getString("frequence_posologie"),
						rs.getString("administration_medicament"),
						rs.getString("categorie_therapeutique_medicament"));
				res.add(medicament);
			}
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}

	@Override
	public Ordonnance getOrdonnance(Integer id_ordonnance)
			throws RemoteException {
		Ordonnance res = null;
		base.ouvrir();
		
		String query = "SELECT * FROM ordonnance WHERE id_ordonnance = ?";
		
		System.out.println("query = "+query);
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			ps.setInt(1, id_ordonnance);
			
			ResultSet rs = ps.executeQuery();
			rs.next();
			res = new Ordonnance(rs.getInt("id_ordonnance"),
					rs.getString("date_ordonnance"),
					rs.getString("description_ordonnance"),
					rs.getString("numero_securite_sociale_patient"),
					rs.getString("id_medecin"), 
					listerMedicamentsOrdonnance(id_ordonnance));
			
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}

	
	public ArrayList<DroitAcces> listerDroitAcces() throws RemoteException {
		ArrayList<DroitAcces> res = new ArrayList<DroitAcces>();
		base.ouvrir();
		
		String query = "SELECT * FROM droit_acces";
		
		PreparedStatement ps = null;
		
		try {
			ps = base.getConnection().prepareStatement(query);
			
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				DroitAcces droitAcces = new DroitAcces(rs.getInt("id_droit_acces"),
						rs.getString("description_droit_acces"),
						rs.getString("valeur_droit_acces"));
				res.add(droitAcces);
			}
			if (ps != null) ps.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		base.fermer();
		return res;
	}
	
}
